package dag

import (
	"bytes"
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strconv"
	"text/template"

	"hxy/repo/cmd"
	"hxy/repo/database"
	lerrors "hxy/repo/error"

	"github.com/google/uuid"
	"github.com/shiningrush/fastflow/pkg/entity/run"
	"gorm.io/gorm"

	"github.com/emicklei/go-restful/v3"
)

type TaksRunCommand interface {
	Run() error
	Build(interface{}) error
}
type DAGPipelineTask struct {
	ID   string
	task PipelineTask
}

// Name define the unique action identity, it will be used by Task
func (dagtask *DAGPipelineTask) Name() string {
	return "DAGPipelineTask"
}

// Attention!: this func is need for parame input . BUG for nil
func (a *DAGPipelineTask) ParameterNew() interface{} {
	return &cmd.RunCommand{}
}
func (dagtask *DAGPipelineTask) Run(ctx run.ExecuteContext, params interface{}) error {

	err := dagtask.task.Build(params)
	if err != nil {
		return err
	}
	database.CreatePipelineTask(dagtask.task)
	database.ChangePipelineState(dagtask.task, "running")
	err = dagtask.task.Run()

	if err != nil {
		database.ChangePipelineState(dagtask.task, "error")
		log.Printf("Error in DAG tasks %s,:%s", dagtask.task.ID, err)
		database.AddTaskEndtiem(dagtask.task)
		return err
	} else {
		database.ChangePipelineState(dagtask.task, "FINISH")
		database.AddTaskEndtiem(dagtask.task)
		return nil
	}

}

type DagTask struct {
	IDName  string    `josn:"id" gorm:"primaryKey"`
	Git_id  int       `json:"Git_id"`
	GitHash string    `json:"GitHash"`
	Dir     string    `json:"Dir"`
	Tasks   TasksList `json:"tasks"`
}
type TaskInterface struct {
	Name     string   `json:"taskname"`
	RunProg  string   `json:"runprog"`
	Command  string   `json:"command"`
	Dependon []string `json:"depend"`
}
type TasksList []TaskInterface

func (tasklist *TasksList) Scan(src any) error {
	bytes, ok := src.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", src))
	}
	err := json.Unmarshal(bytes, &tasklist)
	return err
}
func (tasklist TasksList) Value() (driver.Value, error) {
	re, err := json.Marshal(tasklist)
	return re, err
}

// #region template for fastflow template generation
const dagtemplate = `id: "{{.IDName}}"
name: "{{.IDName}}"
vars:
  gitid:
    desc: "common git id"
    defaultValue: "{{.Git_id}}"
  githash:
    desc: "common git hash"
    defaultValue: "{{.GitHash}}"
  RunDir:
    desc: "command run dir"
    defaultValue: "/home/luxian/k8s/dashboard"
tasks:
`
const tasktemplate = `- id: "{{.Name}}"
  actionName: "DAGPipelineTask"
  params:%s
    RunProg: "{{.RunProg}}"
    Command: "{{.Command}}"
`
const depend_template = `  dependOn:
    {{ range .Dependon }}- "{{ . }}"
{{- end }}
`
const gitinfo = `
    Git_id: "{{gitid}}"
    GitHash: "{{githash}}"
    Dir: "{{RunDir}}"`

// #endregion

// 受限于 golang-template 与 fastflow 之间存在模板冲突，部分迭代关系需要手动维护
func (dagtask DagTask) GenYamlfromInterface() (string, error) {
	var yamlgen = ""
	// 创建一个模板对象
	tmplObj, err := template.New("dag-template").Parse(dagtemplate)
	if err != nil {
		return "", err
	}

	var result bytes.Buffer
	err = tmplObj.Execute(&result, dagtask)
	if err != nil {
		return "", err
	}
	yamlgen += result.String()

	taskObj, err := template.New("dag-task-template").Parse(tasktemplate)
	if err != nil {
		return "", err
	}
	for _, task := range dagtask.Tasks {
		var result bytes.Buffer
		err = taskObj.Execute(&result, task)
		if err != nil {
			return "", err
		}
		yamlgen += fmt.Sprintf(result.String(), gitinfo)
		if len(task.Dependon) > 0 {
			dependPbj, _ := template.New("depend-template").Parse(depend_template)

			var result bytes.Buffer
			err = dependPbj.Execute(&result, task)
			if err != nil {
				return "", err
			}
			yamlgen += result.String()
		}
	}
	return yamlgen, nil
}

var TestcreateDagTask = createDagTask

func createDagTask(dagtask DagTask) *gorm.DB {
	if len(dagtask.IDName) < 5 {
		dagtask.IDName = uuid.NewString()
	}
	return database.Db.Create(&dagtask)
}

func CheckDagTasksStatus(finish <-chan bool, err <-chan error) {

}

func GetaDagtask(gitid int) (DagTask, error) {
	var dag DagTask
	re := database.Db.Where("git_id = ?", gitid).Find(&dag)
	if re.RowsAffected == 0 {
		return DagTask{}, errors.New("can't find dag task by git id")
	}
	return dag, nil
}
func HandlerDagTaskCreate(request *restful.Request, response *restful.Response) {
	var dag DagTask
	if err := request.ReadEntity(&dag); err != nil {
		lerrors.HandleInternalError(response, err)
		return
	}
	re := createDagTask(dag)
	if re.Error != nil {
		lerrors.HandleInternalError(response, re.Error)
		return
	}
	response.WriteAsJson(dag)
}

func HandlerDagTaskGet(request *restful.Request, response *restful.Response) {
	var id = request.PathParameter("gitid")
	aid, _ := strconv.Atoi(id)
	dagtask, err := GetaDagtask(aid)
	if err != nil {
		lerrors.HandleInternalError(response, err)
		return
	}
	response.WriteAsJson(dagtask)
}
